﻿using System;
using System.Net;
using System.IO;
using System.Text;

namespace FileUpload_5.Classes
{
	public class RequestState
	{
		// This class stores the State of the request.
		const int BUFFER_SIZE = 2048;
		public StringBuilder requestData;
		public byte[] BufferRead;
		public HttpWebRequest request;
		public HttpWebResponse response;
		public Stream streamResponse;
		public RequestState()
		{
			BufferRead = new byte[BUFFER_SIZE];
			requestData = new StringBuilder("");
			request = null;
			streamResponse = null;
		}
	}

	public class HttpFileUploader : IFileUploader
	{
		private UserFile _file;
		private long _dataLength;
		private long _dataSent;
		private string _initParams;

		private long ChunkSize = 2097152;//4194304;
		private string UploadUrl;
		private static int BUFFER_SIZE = 2048;

		public HttpFileUploader(UserFile file, string httpHandlerURL)
		{
			_file = file;

			_dataLength = _file.FileStream.Length;
			_dataSent = 0;

			UploadUrl = httpHandlerURL;
		}

		#region IFileUploader Members


		/// <summary>
		/// Start the file upload
		/// </summary>
		/// <param name="initParams"></param>
		public void StartUpload(string initParams)
		{
			_initParams = initParams;

			StartUpload();
		}

		/// <summary>
		/// Cancel the file upload
		/// </summary>
		public void CancelUpload()
		{
			this._file.State = Constants.FileStates.Pause;
		}

		public void ResumeUpload()
		{
			this._file.State = Constants.FileStates.Uploading;

			StartUpload();
		}

		public event EventHandler UploadFinished;

		#endregion

		private void StartUpload()
		{
			long dataToSend = _dataLength - _dataSent;
			bool isLastChunk = dataToSend <= ChunkSize;
			bool isFirstChunk = _dataSent == 0;

			UriBuilder httpHandlerUrlBuilder = new UriBuilder(UploadUrl);
			httpHandlerUrlBuilder.Query = string.Format("{5}file={0}&offset={1}&last={2}&first={3}&param={4}", _file.FileName, _dataSent, isLastChunk, isFirstChunk, _initParams, string.IsNullOrEmpty(httpHandlerUrlBuilder.Query) ? "" : httpHandlerUrlBuilder.Query.Remove(0, 1) + "&");

			HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(httpHandlerUrlBuilder.Uri);
			webRequest.Method = "POST";
			webRequest.BeginGetRequestStream(new AsyncCallback(WriteToStreamCallback), webRequest);

		}

		private void WriteToStreamCallback(IAsyncResult asynchronousResult)
		{
			if (_file.State == Constants.FileStates.Pause || _file.FileStream == null)
			{
				return;
			}

			HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
			Stream requestStream = webRequest.EndGetRequestStream(asynchronousResult);

			byte[] buffer = new Byte[BUFFER_SIZE];
			int bytesRead = 0;
			int tempTotal = 0;

			//Set the start position
			_file.FileStream.Position = _dataSent;

			//Read the next chunk
			while((bytesRead = _file.FileStream.Read(buffer, 0, buffer.Length)) != 0 && tempTotal + bytesRead < ChunkSize && !_file.IsDeleted && _file.State != Constants.FileStates.Error)
			{
				requestStream.Write(buffer, 0, bytesRead);
				requestStream.Flush();

				_dataSent += bytesRead;
				tempTotal += bytesRead;

				//Notify progress change
				_file.UIDispatcher.BeginInvoke(delegate()
				{
					OnProgressChanged();
				});
			}

			//Leave the fileStream OPEN
			//fileStream.Close();

			requestStream.Close();

			//Get the response from the HttpHandler
			//webRequest.BeginGetResponse(new AsyncCallback(ReadHttpResponseCallback), webRequest);

			RequestState myRequestState = new RequestState();
			myRequestState.request = webRequest;

			IAsyncResult result = (IAsyncResult)webRequest.BeginGetResponse(new AsyncCallback(ReadHttpResponseCallback), myRequestState);

			//myRequestState.response.Close();
		}

		private  void RespCallback(IAsyncResult asynchronousResult)
		{
			try
			{
				// State of request is asynchronous.
				RequestState myRequestState = (RequestState)asynchronousResult.AsyncState;
				HttpWebRequest myHttpWebRequest = myRequestState.request;
				myRequestState.response = (HttpWebResponse)myHttpWebRequest.EndGetResponse(asynchronousResult);

				// Read the response into a Stream object.
				Stream responseStream = myRequestState.response.GetResponseStream();
				myRequestState.streamResponse = responseStream;

				// Begin the Reading of the contents of the HTML page and print it to the console.
				IAsyncResult asynchronousInputRead = responseStream.BeginRead(myRequestState.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadHttpResponseCallback), myRequestState);
				return;
			}
			catch(WebException e)
			{
				//Console.WriteLine("\nRespCallback Exception raised!");
				//Console.WriteLine("\nMessage:{0}", e.Message);
				//Console.WriteLine("\nStatus:{0}", e.Status);
			}
		}

		private void ReadHttpResponseCallback(IAsyncResult asynchronousResult)
		{
			bool error = false;

			//Check if the response is OK
			try
			{
				RequestState requestState = (RequestState)asynchronousResult.AsyncState;
				HttpWebResponse webResponse = (HttpWebResponse)requestState.request.EndGetResponse(asynchronousResult);
				StreamReader reader = new StreamReader(webResponse.GetResponseStream());

				string responsestring = reader.ReadToEnd();

				int sentData = 0;

				if(int.TryParse(responsestring, out sentData))
				{
					_dataSent = sentData;
				}

				reader.Close();
			}
			catch
			{
				error = true;

				_file.UIDispatcher.BeginInvoke(delegate()
				{
					_file.State = Constants.FileStates.Error;
				});
			}

			if(_dataSent < _dataLength)
			{
				//Not finished yet, continue uploading
				if((_file.State != Constants.FileStates.Error || _file.State != Constants.FileStates.Pause) && !error)
					StartUpload();
			}
			else
			{
				_file.FileStream.Close();
				_file.FileStream.Dispose();

				//Finished event
				_file.UIDispatcher.BeginInvoke(delegate()
				{
					if(UploadFinished != null)
						UploadFinished(this, null);
				});
			}
		}

		//private static void ReadCallBack(IAsyncResult asyncResult)
		//{
		//  try
		//  {
		//    RequestState myRequestState = (RequestState)asyncResult.AsyncState;
		//    Stream responseStream = myRequestState.streamResponse;
		//    int read = responseStream.EndRead(asyncResult);
		//    // Read the HTML page and then print it to the console.
		//    if(read > 0)
		//    {
		//      myRequestState.requestData.Append(Encoding.UTF8.GetString(myRequestState.BufferRead, 0, read));
		//      IAsyncResult asynchronousResult = responseStream.BeginRead(myRequestState.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), myRequestState);
		//      return;
		//    }
		//    else
		//    {
		//      Console.WriteLine("\nThe contents of the Html page are : ");
		//      if(myRequestState.requestData.Length > 1)
		//      {
		//        string stringContent;
		//        stringContent = myRequestState.requestData.ToString();
		//        Console.WriteLine(stringContent);
		//      }
		//      Console.WriteLine("Press any key to continue..........");
		//      Console.ReadLine();

		//      responseStream.Close();
		//    }

		//  }
		//  catch(WebException e)
		//  {
		//    //Console.WriteLine("\nReadCallBack Exception raised!");
		//    //Console.WriteLine("\nMessage:{0}", e.Message);
		//    //Console.WriteLine("\nStatus:{0}", e.Status);
		//  }
		//}

		private void OnProgressChanged()
		{
			_file.BytesUploaded = _dataSent;
		}
	}
}
